springcloud |
您所在的位置:网站首页 › redis scan match很慢 › springcloud |
业务场景
一次性取出redis一个field中的所有key,并遍历。 方案一使用redisTemplate.opsForHash().keys("filed") 前期数据量少,未感知到性能问题。后查询资料得知,数据量上去后keys方法严重消耗CPU,一般在生产环境禁用keys方法。 防患于未然,该方式摒弃! 方案二使用redisTemplate游标分批次获取 使用scan主要两个参数:match和count。 match:key的正则表达式 count:每次扫描的记录数。值越小,扫描次数越过、越耗时。建议设置在1000-10000 public void getKeysTest(){ try { Cursor cursor = deviceRedis.opsForHash().scan("filed", ScanOptions.scanOptions().match("*").count(1000).build()); while (cursor.hasNext()) { String key = cursor.next().getKey() Set valueSet = cursor.next().getValue(); } } catch (IOException e) { e.printStackTrace(); } }使用scan代替keys肯定会导致整个查询消耗的总时间变大,但不会影响redis服务卡顿,影响服务使用。 后记使用scan代替keys方式后,发现仅测试环境如此低的并发和数据量情况下,redis却经常会报错: Could not get a resource from the pool !重启后回复正常,然后又重复! 问题定位代码中对于redis的存、取、删除、过期设置、游标等操作都有涉及,最开始始终无法定位具体哪里的原因。 以为是RedisTemplate从JedisPool中获取连接使用后没有释放链接造成的。 但RedisTemplate是对Jedis做了二次封装,可自动通过连接池来管理连接。解读RedisTemplate源码后,确认了这一点: RedisTemplate封装的redis操作方法,大多方法最底层都会调用最核心的excute方法,在excute方法的finally模块可以看到每次操作完成后都会自动关闭释放连接。该原因剔除! 最后通过调低连接池的max-active,多次的测试、重现,最终确认问题的原因是由于调用了scan后redis连接数只升不降。 通过redis-cli进入redis控制台,使用CLIENT LIST命令查看redis客户端连接信息: 其中,age标识已建立连接的时长(单位:秒),cmd标识操作命令。 正常情况下,redis连接信息中99%只应显示ping和auth命令,因为其他redis操作都是操作完成后立即释放连接,但是从上图看到scan命令对应连接的连接时长远高于其他操作,说明连接一直未断开。后来又测试几次,发现scan操作的连接信息只升不降。 最终问题终于定位le:scan操作后,连接没有释放,导致连接池可用连接被用完! 解决方式又查看到scan的源码: @Override public Cursor scan(K key, final ScanOptions options) { final byte[] rawKey = rawKey(key); return template.executeWithStickyConnection(new RedisCallback() { @Override public Cursor doInRedis(RedisConnection connection) throws DataAccessException { return new ConvertingCursor(connection.hScan(rawKey, options), new Converter() { @Override public Entry convert(final Entry source) { return new Map.Entry() { @Override public HK getKey() { return deserializeHashKey(source.getKey()); } @Override public HV getValue() { return deserializeHashValue(source.getValue()); } @Override public HV setValue(HV value) { throw new UnsupportedOperationException("Values cannot be set when scanning through entries."); } }; } }); } });没有看到操作完成后关闭/释放/归还连接(或者我没找到吧)。 只能通过手动关闭来实现: public void getKeysTest(){ try { Cursor cursor = deviceRedis.opsForHash().scan("filed", ScanOptions.scanOptions().match("*").count(1000).build()); while (cursor.hasNext()) { String key = cursor.next().getKey() Set valueSet = cursor.next().getValue(); } //关闭scan cursor.close(); } catch (IOException e) { e.printStackTrace(); } }最后问题解决了,未再出现Could not get a resource from the pool!错误。 与各位共勉! |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |